/* timeserv.c */
/* A simple UDP server that sends the current date and time to the client */
/* Last modified: September 20, 2005 */
/* http://www.gomorgan89.com */
/* Link with library file wsock32.lib */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <winsock.h>
#include "Util.h"
#include "Listener.h"
#include "NetworkDGram.h"

#pragma comment (lib, "wsock32.lib")

#define BUFFER_SIZE 4096

#define SERVER_IP		"127.0.0.1"
#define CLIENT_IP		"127.0.0.1"
//#define SERVER_IP		"76.126.33.146"
//#define CLIENT_IP		"192.168.1.105"
#define NETWORK_PORT	"6665"

bool	wsaStarted=false;
WSADATA wsaData;							/* Used to open windows connection */

int	startNetwork(){
	if ((!wsaStarted) && (WSAStartup(0x0101, &wsaData) != 0))
	{
		logstr("Could not open Windows connection.\n");
		return 0;
	}else
		wsaStarted=true;
	return 1;

}

int	stopNetwork(){
	if(wsaStarted)
		if(WSACleanup()!=0)return 0;
	wsaStarted=false;
	return 1;
}

void serverUsage(void);

class	Connection{
public:
	SOCKET		tcpSocket;
	sockaddr	address;
	bool		broken;
	std::vector<NetworkDGram>	outgoingUDP;
	std::vector<NetworkDGram>	outgoingTCP;
	std::vector<NetworkDGram>	incoming;
};


class	EndPoint{
public:
	SOCKET	udpSocket;							/* UPD */
	SOCKET	tcpSocket;							/* TCP socket. on server this is the listen socket*/
	void	closeAndCleanup(){
		closesocket(udpSocket);
		closesocket(tcpSocket);
		stopNetwork();
	}
};

class	Server : public EndPoint{
public:
	unsigned short port_number;			/* Port number to use */
	int		a1, a2, a3, a4;					/* Components of address in xxx.xxx.xxx.xxx form */
	int		client_length;					/* Length of client struct */
	int		bytes_received;					/* Bytes received from client */
	sockaddr_in server;					/* Information about the server */
	struct	sockaddr_in client;			/* Information about the client */
	char	buffer[BUFFER_SIZE];			/* Where to store received data */
	struct	hostent *hp;					/* Information about this computer */
	char	host_name[256];				/* Name of the server */
	bool	started;
	std::vector<Connection>	connections;
	NetworkDGram	dgram;
	Server(){
		started=false;
	}
	int start(int argc, char **argv)
	{
		/* Interpret command line */
		if (argc == 2)
		{
			/* Use local address */
			if (sscanf_s(argv[1],"%u", &port_number) != 1)
			{
				serverUsage();
				return 0;
			}
		}
		else if (argc == 3)
		{
			/* Copy address */
			if (sscanf_s(argv[1], "%d.%d.%d.%d", &a1, &a2, &a3, &a4) != 4)
			{
				serverUsage();
				return 0;
			}
			if (sscanf_s(argv[2], "%u", &port_number) != 1)
			{
				serverUsage();
				return 0;
			}
		}
		else
		{
			serverUsage();
			return 0;
		}

		/* Open windows connection */
		if(!startNetwork())return 0;

		/* Open a datagram socket */
		udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
		if (udpSocket == INVALID_SOCKET)
		{
			logstr("Could not create UDP socket.\n");
			stopNetwork();
			return 0;
		}
		tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
		if (tcpSocket == INVALID_SOCKET)
		{
			logstr("Could not create TCP socket.\n");
			closesocket(udpSocket);
			stopNetwork();
			return 0;
		}

		/* Clear out server struct */
		memset((void *)&server, '\0', sizeof(struct sockaddr_in));

		/* Set family and port */
		server.sin_family = AF_INET;
		server.sin_port = htons(port_number);

		/* Set address automatically if desired */
		if (argc == 2)
		{
			/* Get host name of this computer */
			gethostname(host_name, sizeof(host_name));
			hp = gethostbyname(host_name);

			/* Check for NULL pointer */
			if (hp == NULL)
			{
				logstr( "Could not get host name.\n");
				closeAndCleanup();
				return 0;
			}
		
			/* Assign the address */
			server.sin_addr.S_un.S_un_b.s_b1 = hp->h_addr_list[0][0];
			server.sin_addr.S_un.S_un_b.s_b2 = hp->h_addr_list[0][1];
			server.sin_addr.S_un.S_un_b.s_b3 = hp->h_addr_list[0][2];
			server.sin_addr.S_un.S_un_b.s_b4 = hp->h_addr_list[0][3];
		}
		/* Otherwise assign it manually */
		else
		{
			server.sin_addr.S_un.S_un_b.s_b1 = (unsigned char)a1;
			server.sin_addr.S_un.S_un_b.s_b2 = (unsigned char)a2;
			server.sin_addr.S_un.S_un_b.s_b3 = (unsigned char)a3;
			server.sin_addr.S_un.S_un_b.s_b4 = (unsigned char)a4;
		}

		/* Bind address to socket */
		if (bind(udpSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1)
		{
			logstr("Could not bind name to UDP socket.\n");
			closeAndCleanup();
			return 0;
		}
		u_long iMode=1;
		int iResult=ioctlsocket(udpSocket,FIONBIO,&iMode);
		if(iResult!=NO_ERROR){
			logstr(format("ioctlsocket udp failed with error: %ld\n", iResult));
			closeAndCleanup();
			return 0;
		}		
		
		/* Bind address to socket */
		if (bind(tcpSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1)
		{
			logstr("Could not bind name to TCP socket.\n");
			closeAndCleanup();
			return 0;
		}

		iResult=listen(tcpSocket,5);
		if(iResult!=NO_ERROR){
			logstr(format("listen tcp failed with error: %ld\n", iResult));
			closeAndCleanup();
			return 0;
		}
		
		iMode=1;
		iResult=ioctlsocket(tcpSocket,FIONBIO,&iMode);
		if(iResult!=NO_ERROR){
			logstr(format("ioctlsocket tcp failed with error: %ld\n", iResult));
			closeAndCleanup();
			return 0;
		}

		/* Print out server information */
		logstr(format("Server running on %u.%u.%u.%u\n", (unsigned char)server.sin_addr.S_un.S_un_b.s_b1,
												  (unsigned char)server.sin_addr.S_un.S_un_b.s_b2,
												  (unsigned char)server.sin_addr.S_un.S_un_b.s_b3,
												  (unsigned char)server.sin_addr.S_un.S_un_b.s_b4));
		//logstr("Press CTRL + C to quit\n");

		/* Loop and get data from clients */
		//while (1)
		{
		}
		started=true;
		return 1;
	}
	void	broadcastTCP(NetworkDGram*	dgram){
		for(unsigned int i=0;i<connections.size();i++){
			connections[i].outgoingTCP.push_back(*dgram);
		}
	}
	void	broadcastUDP(NetworkDGram*	dgram){
		for(unsigned int i=0;i<connections.size();i++){
			connections[i].outgoingUDP.push_back(*dgram);
		}
	}
	int	update(){
		if(!started)return 1;
		
		FD_SET	readables;
		FD_SET	writeables;
		FD_ZERO(&readables);
	    FD_ZERO(&writeables);

      // Check for available data, and connection attempts.

		FD_SET(tcpSocket, &readables);
		for (unsigned int i = 0; i < connections.size(); i++){
			if(connections[i].broken)
				continue; //Skip broken connections...
			if (connections[i].outgoingTCP.size() > 0)
				FD_SET(connections[i].tcpSocket, &writeables);

			FD_SET(connections[i].tcpSocket, &readables);
		}
		timeval tm;
		tm.tv_sec=0;
		tm.tv_usec=1;
		int totalSet = select(0, &readables, &writeables, NULL, &tm);
		if (totalSet == SOCKET_ERROR)
		{
			logstr(format("select function returned with error %d\n", WSAGetLastError()));
			closeAndCleanup();
			started=false;
			return 0;
		}
		if(totalSet>0){
			if (FD_ISSET(tcpSocket, &readables)){
				Connection nc;
				nc.broken=false;
				totalSet--;
				logstr(format("Server got TCP connection attempt.\n"));
				int asz=sizeof(nc.address);
				nc.tcpSocket=accept(tcpSocket,&nc.address,&asz);
				if(nc.tcpSocket != INVALID_SOCKET){
					u_long NonBlock = 1;
					if (ioctlsocket(nc.tcpSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)
					{
					   logstr(format("Server accept ioctlsocket(FIONBIO) failed with error %d. discarding connection.\n", WSAGetLastError()));
					   return 0;
					}else{
						connections.push_back(nc);
						logstr(format("Server accepted connections %14x.\n",&nc.address.sa_data));
					}
				}else{
					logstr(format("Server got error accepting connection attempt.\n"));
				}
			}
		}
		for (unsigned int i = 0;(totalSet>0) && (i < connections.size()); i++){
			Connection& c=connections[i];
			if(FD_ISSET(c.tcpSocket,&readables)){
				totalSet--;
				int asz=sizeof(sockaddr);
				sockaddr from;
				int recvct=recvfrom(c.tcpSocket,(char*)&dgram,sizeof(dgram),0,&from,&asz);
				if(recvct!=sizeof(dgram)){
					//Mark this connection as dead.
					logstr(format("Server failed to recvfrom or got partial packet. Dropping connection.\n",recvct));
					closesocket(c.tcpSocket);
					c.broken=true;
				}else{
					logstr(format("Server got datagram:%s of sz:%d\n",(char*)&dgram,recvct));
					Listener::sendMessage("networkDataFromClient",&dgram);
				}
			}else if(FD_ISSET(c.tcpSocket,&writeables)){
				totalSet--;
				for(unsigned int t=0;t<c.outgoingTCP.size();t++){
					int asz=sizeof(sockaddr);
					sockaddr from;
					if(sendto(c.tcpSocket,(char*)&c.outgoingTCP[t],sizeof(NetworkDGram),0,&from,asz)!=sizeof(NetworkDGram)){
						logstr(format("Server failed to send. Dropping connection.\n"));
						closesocket(c.tcpSocket);
						c.broken=true;
					}else{
					}
				}
				c.outgoingTCP.clear();
			}
		}

		/* Receive UDP packets from clients */
		client_length = (int)sizeof(struct sockaddr_in);
		bytes_received = recvfrom(udpSocket, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client, &client_length);
		
		int nError=WSAGetLastError();
		if(nError!=WSAEWOULDBLOCK&&nError!=0)
		//if (bytes_received < 0)
		{
			logstr("Server error: Could not receive datagram.\n");
			closeAndCleanup();
			started=false;
			return 0;
		}
		if(bytes_received>0){
			/* Check for time request */
			if (strcmp(buffer, "time") == 0)
			{
				/* Get current time */
				dgram.time.current_time = time(NULL);
				MakeNetworkDGram(dgram,time);
				/* Send data back */
				if (sendto(udpSocket, (char *)&dgram, (int)sizeof(dgram), 0, (struct sockaddr *)&client, client_length) != (int)sizeof(dgram))
				{
					logstr("Server Error sending datagram.\n");
					closeAndCleanup();
					started=false;
					return 0;
				}
			}else if(strcmp(buffer,"login")){
				
				Listener::sendMessage("networkClientLogin");
				/* Send data back */
				MakeNetworkDGram(dgram,welcome);
				dgram.welcome.userID=100;
				if (sendto(udpSocket, (char *)&dgram, (int)sizeof(dgram), 0, (struct sockaddr *)&client, client_length) != (int)sizeof(dgram))
				{
					logstr("Server Error sending welcome datagram to client.\n");
				}
			}else{
			}
		}
		return 1;
	}
	
	int	stop(){
		if(!started)return 1;
		closeAndCleanup();
		return 1;
	}

	void serverUsage(void)
	{
		logstr("timeserv [server_address] port\n");
		exit(0);
	}
};


#define SIZE 500


class	Client : public EndPoint{
public:
	unsigned short port_number;				/* The port number to use */
	int		server_length;						/* Length of server struct */
	NetworkDGram	dgram;	
	struct	hostent *hp;						/* Information about the server */
	struct	sockaddr_in server;				/* Information about the server */
	struct	sockaddr_in client;				/* Information about the client */
	int		a1, a2, a3, a4;						/* Server address components in xxx.xxx.xxx.xxx form */
	int		b1, b2, b3, b4;						/* Client address components in xxx.xxx.xxx.xxx form */
	char	host_name[256];					/* Host name of this computer */
	bool	started;
	bool	connecting;
	int		connectionAttempts;
	std::vector<NetworkDGram>	outgoingUDP;
	std::vector<NetworkDGram>	outgoingTCP;

	int start(int argc, char **argv)
	{

		/* Make sure command line is correct */
		if (argc != 3 && argc != 4)
		{
			clientUsage();
			return 0;
		}
		if (sscanf_s(argv[1], "%d.%d.%d.%d", &a1, &a2, &a3, &a4) != 4)
		{
			clientUsage();
			return 0;
		}
		if (sscanf_s(argv[2], "%u", &port_number) != 1)
		{
			clientUsage();
			return 0;
		}
		if (argc == 4)
		{
			if (sscanf_s(argv[3], "%d.%d.%d.%d", &b1, &b2, &b3, &b4) != 4)
			{
				clientUsage();
				return 0;
			}
		}

		/* Open windows connection */
		if(!startNetwork())return 0;
	
		/* Open a datagram socket */
		udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
		if (udpSocket == INVALID_SOCKET)
		{
			logstr("Could not UDP create socket.\n");
			stopNetwork();
			return 0;
		}
		tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
		if (tcpSocket == INVALID_SOCKET)
		{
			logstr("Could not TCP create socket.\n");
			closesocket(udpSocket);
			stopNetwork();
			return 0;
		}

		/* Clear out server struct */
		memset((void *)&server, '\0', sizeof(struct sockaddr_in));

		/* Set family and port */
		server.sin_family = AF_INET;
		server.sin_port = htons(port_number);

		/* Set server address */
		server.sin_addr.S_un.S_un_b.s_b1 = (unsigned char)a1;
		server.sin_addr.S_un.S_un_b.s_b2 = (unsigned char)a2;
		server.sin_addr.S_un.S_un_b.s_b3 = (unsigned char)a3;
		server.sin_addr.S_un.S_un_b.s_b4 = (unsigned char)a4;

		/* Clear out client struct */
		memset((void *)&client, '\0', sizeof(struct sockaddr_in));

		/* Set family and port */
		client.sin_family = AF_INET;
		client.sin_port = htons(0);

		if (argc == 3)
		{
			/* Get host name of this computer */
			gethostname(host_name, sizeof(host_name));
			hp = gethostbyname(host_name);

			/* Check for NULL pointer */
			if (hp == NULL)
			{
				logstr("Could not get host name.\n");
				closeAndCleanup();
				return 0;
			}

			/* Assign the address */
			client.sin_addr.S_un.S_un_b.s_b1 = hp->h_addr_list[0][0];
			client.sin_addr.S_un.S_un_b.s_b2 = hp->h_addr_list[0][1];
			client.sin_addr.S_un.S_un_b.s_b3 = hp->h_addr_list[0][2];
			client.sin_addr.S_un.S_un_b.s_b4 = hp->h_addr_list[0][3];
		}
		else
		{
			client.sin_addr.S_un.S_un_b.s_b1 = (unsigned char)b1;
			client.sin_addr.S_un.S_un_b.s_b2 = (unsigned char)b2;
			client.sin_addr.S_un.S_un_b.s_b3 = (unsigned char)b3;
			client.sin_addr.S_un.S_un_b.s_b4 = (unsigned char)b4;
		}

		/* Bind local address to socket */
		if (bind(udpSocket, (struct sockaddr *)&client, sizeof(struct sockaddr_in)) == -1)
		{
			logstr("Cannot bind address to socket.\n");
			closeAndCleanup();
			return 0;
		}

		u_long iMode=1;
		int iResult=ioctlsocket(udpSocket,FIONBIO,&iMode);
		if(iResult!=NO_ERROR){
			logstr(format("ioctlsocket udp failed with error: %ld\n", iResult));
			closeAndCleanup();
			return 0;
		}
		
		/* Bind local address to socket */
		if (bind(tcpSocket, (struct sockaddr *)&client, sizeof(struct sockaddr_in)) == -1)
		{
			logstr("Cannot bind address to tcp socket.\n");
			closeAndCleanup();
			return 0;
		}
		
		server_length = sizeof(struct sockaddr_in);
		iResult=connect(tcpSocket,(struct sockaddr *)&server,server_length);
		if(iResult!=NO_ERROR){
			logstr(format("connect tcp failed with error: %ld\n", iResult));
			closeAndCleanup();
			return 0;
		}

		iMode=1;
		iResult=ioctlsocket(tcpSocket,FIONBIO,&iMode);
		if(iResult!=NO_ERROR){
			logstr(format("ioctlsocket tcp failed with error: %ld\n", iResult));
			closeAndCleanup();
			return 0;
		}
		started=true;
		connecting=true;
		connectionAttempts=0;
		return 1;
	}
	int sendDataToServer(SOCKET&		ssocket,std::vector<NetworkDGram>&	queue){		
		if(queue.size()!=0){
			for(unsigned int t=0;t<queue.size();t++){
				server_length = sizeof(struct sockaddr_in);
				int sendct=sendto(ssocket,(char*) &queue[t], (int)sizeof(NetworkDGram), 0, (struct sockaddr *)&server, server_length);
				int nError=WSAGetLastError();
				if(nError!=WSAEWOULDBLOCK&&nError!=0)
				{
					logstr(format("Client Error sending %s data.\n",((&ssocket)==(&tcpSocket))?"TCP":"UDP"));
					closeAndCleanup();
					started=false;
					return 0;
				}
			}
			queue.clear();
		}
		return 1;
	}
	int	update(){
		if(!started)return 1; 
		static int skipcount=0;
		if(skipcount!=0){
			skipcount--;
			return 1;
		}
		skipcount=120;
		
/*		fd_set readfds
                        __in int nfds,
                        __inout_opt fd_set FAR *readfds,
                        __inout_opt fd_set FAR *writefds,
                        __inout_opt fd_set FAR *exceptfds,
                        __in_opt  const struct timeval FAR *timeout);
		select( */
		if(connecting){
			if(connectionAttempts>3){
				closeAndCleanup();
				started=false;
				connectionAttempts=0;
				connecting=false;
				Listener::sendMessage("networkFailToConnect");
				return 1;
			}
			else{
				logstr("Attempting to connect to server...");
				connectionAttempts++;
				char send_buffer[SIZE] = "CONNECT\r\n";/* Data to send */
				/* Tranmsit data to get time */
				server_length = sizeof(struct sockaddr_in);
				int sendct=sendto(udpSocket, send_buffer, (int)strlen(send_buffer) + 1, 0, (struct sockaddr *)&server, server_length);

				int nError=WSAGetLastError();
				if(nError!=WSAEWOULDBLOCK&&nError!=0)
				{
					logstr("Error transmitting data.\n");
					closeAndCleanup();
					started=false;
					connectionAttempts=0;
					connecting=false;
					Listener::sendMessage("networkFailToConnect");
					return 0;
				}

				/* Receive response */

				int recvct=recvfrom(udpSocket, (char *)&dgram, (int)sizeof(dgram), 0, (struct sockaddr *)&server, &server_length);
		
				nError=WSAGetLastError();
				if(nError!=WSAEWOULDBLOCK&&nError!=0)
				{
					logstr("Client Error receiving data.\n");
					closeAndCleanup();
					started=false;
					Listener::sendMessage("networkIOError");
					return 0;
				}
				if(recvct==sizeof(dgram)){
					/* Display time */
					char*	buf=(char*)&dgram;
					if(strcmp("welcome",buf)==0){
						logstr("Client Got welcome response.");
						connectionAttempts=0;
						connecting=false;
						Listener::sendMessage("networkConnectedAsClient");
					}
				}
			}
		}else{
			if(!sendDataToServer(udpSocket,outgoingUDP))return 0;
			if(!sendDataToServer(tcpSocket,outgoingTCP))return 0;

			/* Receive incoming TCP*/
			int recvct=recvfrom(tcpSocket, (char *)&dgram, (int)sizeof(dgram), 0, (struct sockaddr *)&server, &server_length);		
			int nError=WSAGetLastError();
			if(nError!=WSAEWOULDBLOCK&&nError!=0)
			{
				logstr("Client Error receiving data.\n");
				closeAndCleanup();
				started=false;
				return 0;
			}
			if(recvct==(int)sizeof(dgram)){
				if(strcmp((char*)&dgram,"time")==0){
					/* Display time */
					char buf[256];
					ctime_s(&buf[0],256,&dgram.time.current_time);
					logstr(format("Current time: %s", &buf[0]));
				}else{
					Listener::sendMessage("networkDataFromServer",&dgram);
					//logstr(format("Unrecognized or OO packet: %s", &dgram));
				}
			}else if(recvct>0){
				logstr(format("Got incomplete packet sized: %d", recvct));
			}

#if 0
			MakeNetworkDGram(dgram,time);
			dgram.time.current_time = time(NULL);/* Data to send */
			/* Tranmsit data to get time */
			server_length = sizeof(struct sockaddr_in);
			int sendct=sendto(udpSocket,(char*) &dgram, (int)sizeof(dgram), 0, (struct sockaddr *)&server, server_length);

			int nError=WSAGetLastError();
			if(nError!=WSAEWOULDBLOCK&&nError!=0)
			{
				logstr("Error transmitting data.\n");
				closeAndCleanup();
				started=false;
				return 0;
			}

			/* Receive time */
			int recvct=recvfrom(udpSocket, (char *)&dgram, (int)sizeof(dgram), 0, (struct sockaddr *)&server, &server_length);
		
			nError=WSAGetLastError();
			if(nError!=WSAEWOULDBLOCK&&nError!=0)
			{
				logstr("Client Error receiving data.\n");
				closeAndCleanup();
				started=false;
				return 0;
			}
			if(recvct==(int)sizeof(dgram)){
				/* Display time */
				if(strcmp((char*)&dgram,"time")==0){
					char buf[256];
					ctime_s(&buf[0],256,&dgram.time.current_time);
					logstr(format("Current time: %s", &buf[0]));
				}else{
					logstr(format("Stray or OO packet: %s", &dgram));
				}
			}
#endif
		}
		return 1;
	}

	bool	stop(){
		if(!started)
			return 1;
		closeAndCleanup();
		return 1;
	}

	void clientUsage(void)
	{
		logstr("Client Usage: timecli server_address port [client_address]\n");
		exit(0);
	}
	Client(){
		started=false;
		connecting=false;
		connectionAttempts=0;
	}
};

Server	server;
Client	client;

class NetworkListener : public Listener
{
public:
	virtual bool	message(const char *msg=null,void*	params=null){
		if(msg=="networkSendToServerTCP"){
			if(client.started){
				client.outgoingTCP.push_back(*(NetworkDGram*)params);
			}
		}else if(msg=="networkSendToClientsTCP"){
			if(server.started){
				server.broadcastTCP((NetworkDGram*)params);
			}
		}else if(msg=="networkStart"){

		}else if(msg=="networkUpdate"){
			if(!server.update()){
				Listener::sendMessage("networkServerIOFailure");
			}
			if(!client.update()){
				Listener::sendMessage("networkClientIOFailure");
			}
		}else if(msg=="networkShutdown"){
			if(!client.stop())
				Listener::sendMessage("networkClientIOFailure");
			else Listener::sendMessage("networkClientStopped");
			
			if(!server.stop())
				Listener::sendMessage("networkServerIOFailure");
			else Listener::sendMessage("networkServerStopped");

		}else if(msg=="networkStartClient"){
			char*	args[]={"client",SERVER_IP,NETWORK_PORT,CLIENT_IP};//"76.126.33.146",
			if(!client.start(4,args)){
				Listener::sendMessage("networkFailToConnect");
			}else{
				Listener::sendMessage("networkConnectingToServer");
			}
		}else if(msg=="networkStartServer"){
			char*	args[]={"server",SERVER_IP,NETWORK_PORT};
			if(!server.start(3,args)){
				Listener::sendMessage("networkFailToStartServer");
			}else{
				Listener::sendMessage("networkServerStarted");
			}
		}
		return true;
	}
	NetworkListener(){
		Listener::listen(this,"networkStart");
		Listener::listen(this,"networkUpdate");
		Listener::listen(this,"networkShutdown");
		Listener::listen(this,"networkStartServer");
		Listener::listen(this,"networkStartClient");
		

		Listener::listen(this,"networkSendToClientsTCP");
		Listener::listen(this,"networkSendToServerTCP");

		/*
		networkConnectedAsClient*
		networkConnectingToServer*
		networkServerStarted*

		networkDataFromClient
		networkDataFromServer
		networkClientDropped

		networkFailToConnect*
		networkFailToStartServer*
		networkIOFailure*
		networkClientStopped*
		networkServerStopped*
		*/
	}
};

NetworkListener	gNetworkListener;